#include "SendAudioMsg.h"

extern QUEUE_DATA<MESG> g_sendQueue;

SendAudioMsg::SendAudioMsg(QObject *parent)
    : QObject(parent),
      m_recvBuff(new char[2 * MB])
{
    QAudioFormat format;
    format.setSampleRate(8000);
    format.setSampleSize(16);
    format.setSampleType(QAudioFormat::UnSignedInt);
    format.setChannelCount(1);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);

    auto inputDevice = QAudioDeviceInfo::defaultInputDevice();
    if (!inputDevice.isFormatSupported(format)) {
        LOG << "default device is'nt support format" << inputDevice.deviceName();
        format = inputDevice.nearestFormat(format);
    }
    m_audioInput = new QAudioInput(format, this);
    connect(m_audioInput, &QAudioInput::stateChanged, this, &SendAudioMsg::handleStateChanged);
}

SendAudioMsg::~SendAudioMsg()
{
    m_audioInput->deleteLater();
    delete m_recvBuff;
}

void SendAudioMsg::onStartCollect()
{
    if (m_audioInput->state() == QAudio::ActiveState) {
        return;
    }
    LOG << "start collecting audio";
    m_inputDevice = m_audioInput->start();
    connect(m_inputDevice, &QIODevice::readyRead, this, &SendAudioMsg::onReadyRead);
}

void SendAudioMsg::onStopCollect()
{
    if (m_audioInput->state() == QAudio::StoppedState) {
        return;
    }
    disconnect(m_inputDevice, &QIODevice::readyRead, this, &SendAudioMsg::onReadyRead);
    LOG << "stop collecting audio";
    m_audioInput->stop();
    m_inputDevice = nullptr;
}

void SendAudioMsg::onReadyRead()
{
    static int count = 1, recvBytes = 0;

    if (nullptr == m_inputDevice) {
        return;
    }
    recvBytes += m_inputDevice->read(m_recvBuff + recvBytes, 2 * MB - recvBytes);
    if (count <= 2) {
        count++;
        return;
    }

    try {
        MESG* msg = new MESG;

        memset(msg, 0, sizeof(MESG));
        msg->type = MsgType::MsgType_AudioSend;

        QByteArray recvData(m_recvBuff, recvBytes);
        QByteArray compressData(qCompress(recvData).toBase64());
        LOG << "recvData size " << recvData.size();
        LOG << "compress data size " << compressData.size();

        msg->size = compressData.size();
        try {
            msg->data.reset(new uchar[msg->size]);
            memset(msg->data.get(), 0, msg->size);
            memcpy_s(msg->data.get(), msg->size, compressData, compressData.size());
            g_sendQueue.push_msg(msg);
        } catch (std::bad_alloc& dataError) {
            delete msg;
            LOG << "msg->data new fail" << dataError.what();
        } catch (...){
            delete msg;
            LOG << "An unexpected exception occurred";
        }
    } catch (std::bad_alloc& msgError) {
        LOG << "msg new fail" << msgError.what();
    }
    recvBytes = 0, count = 0;
}

void SendAudioMsg::handleStateChanged(QAudio::State state)
{
    switch(state) {
    case QAudio::ActiveState:
        LOG << "start recording";
    case QAudio::StoppedState:
        if (m_audioInput->error() != QAudio::NoError) {
            onStopCollect();
            emit audioInputError(errorString());
        } else {
            LOG << "stop recording";
        }
    }
}

QString SendAudioMsg::errorString()
{
    switch (m_audioInput->error()) {
    case QAudio::OpenError:
        return QString("AudioInput An error occurred opening the audio device").toUtf8();
    case QAudio::IOError:
        return QString("AudioInput An error occurred during read/write of audio device").toUtf8();
    case QAudio::UnderrunError:
        return QString("AudioInput Audio data is not being fed to the audio device at a fast enough rate").toUtf8();
    case QAudio::FatalError:
        return QString("AudioInput A non-recoverable error has occurred, the audio device is not usable at this time.").toUtf8();
    default:
        return QString("AudioInput No errors have occurred").toUtf8();
    }
}

void SendAudioMsg::setVolumn(int volumn)
{
    m_audioInput->setVolume(volumn);
}
